﻿using System;
using Procure.Data.Context;
using System.Linq;
using System.Data.Linq;
using System.Diagnostics;
using System.Web;
using Procure.Data;

namespace Procure.Library
{
    public class CommerceHelper : Object
    {
        public string AddToCartInputName { get { return "i"; } }
        public string UpdateInputName { get { return "update"; } }
        public string RemoveInputName { get { return "remove"; } }
        public string QuantityInputName { get { return "pq"; } }
        public string ProductIdInputName { get { return "pid"; } }
        public string ClearInputName { get { return "clear"; } }
        public string UpdateAllInputName { get { return "updateAll"; } }

        private enum UpdateCartMode
        {
            Increment = 0,
            Overwrite = 1
        }

        #region GetCurrentCart
        public Cart GetCurrentCart()
        {
            return GetCurrentCart(null);
        }
        public Cart GetCurrentCart(ProcureDataContext dataContext)
        {
            // some trace info that is usefull in debugging
            HttpContext.Current.Trace.Write("CommerceHelper", "Getting current cart");
            Cart cart = null;

            HttpContext context = HttpContext.Current;
            // if the user name is not populated, get the cart from a cookie
            if (string.IsNullOrEmpty(HttpContext.Current.User.Identity.Name))
                cart = GetAnonymousCart(dataContext);
            else
                // otherwise, get the cart more efficiently
                cart = GetLoggedInCart(dataContext);

            return cart;
        }
        #endregion

        #region GetAnonymousCart
        private Cart GetAnonymousCart(ProcureDataContext dataContext)
        {
            User anonymousUser = null;
            Cart cart = null;
            Guid cartGuid = GetCartGuid();

            // if we are not working with a pre-existing data context, new one up
            if (dataContext == null)
                dataContext = DataFactory.GetDataContext();

            try
            {
                // find the current user based on the cart guid
                anonymousUser = dataContext.Users.FirstOrDefault(u => ((u.Username == cartGuid.ToString()) && (u.IsAnonymous)));

                // if the user could be found, get the cart
                if (anonymousUser != null)
                {
                    cart = dataContext.Carts.FirstOrDefault(c => c.UserId == anonymousUser.Id);
                }
                else
                {
                    //otherwise, create a new user
                    anonymousUser = CreateNewUser(dataContext, cartGuid.ToString(), true);
                }
            }
            // catch any exception that occurs and log it to trace
            catch (Exception ex) { HttpContext.Current.Trace.Write("CommerceHelper", "  Exception Getting Cart.", ex); }

            // if a cart wasn't retrieved for some reason
            if (cart == null)
            {
                // create a new cart
                cart = new Cart();
                cart.CreatedOn = DateTime.Now;
                cart.UpdatedOn = cart.CreatedOn;
                cart.User = anonymousUser;

                // mark it to be saved on submit
                dataContext.Carts.InsertOnSubmit(cart);
                // submit the database changes
                dataContext.SubmitChanges();
            }

            // return the cart to the calling function
            return cart;
        }
        #endregion

        #region GetLoggedInCart
        private Cart GetLoggedInCart(ProcureDataContext dataContext)
        {
            User user = null;
            Cart cart = null;

            if (dataContext == null)
                dataContext = DataFactory.GetDataContext();

            try
            {
                // pull the user based on their login name
                user = dataContext.Users.FirstOrDefault(u => ((u.Username == HttpContext.Current.User.Identity.Name) && (u.IsAnonymous == false)));

                // if the user is retrieved, pull their cart based on their user id
                if (user != null)
                {
                    cart = dataContext.Carts.FirstOrDefault(c => c.UserId == user.Id);
                }
                else
                {
                    // if a user was created in the umbraco admin then we may need to create our user record
                    user = CreateNewUser(dataContext, HttpContext.Current.User.Identity.Name, false);
                }
            }
            catch (Exception ex) { HttpContext.Current.Trace.Write("CommerceHelper", "  Exception Getting Cart.", ex); }

            // just in case a user gets created in the umbraco admin - we may need to create a cart
            if (cart == null)
            {
                // create a new cart
                cart = new Cart();
                cart.CreatedOn = DateTime.Now;
                cart.UpdatedOn = cart.CreatedOn;
                cart.User = user;

                // mark the cart to be inserted on submit
                dataContext.Carts.InsertOnSubmit(cart);
                // submit the database changes
                dataContext.SubmitChanges();
            }

            return cart;
        }
        #endregion

        #region CreateNewUser
        private static User CreateNewUser(ProcureDataContext dataContext, string Username, bool IsAnonymous)
        {
            // new up the object
            User user = new User();
            // populate the requisite fields
            user.ApplicationName = "procure";
            user.Username = Username;
            user.IsAnonymous = IsAnonymous;
            user.LastActiveOn = DateTime.Now;
            user.LastUpdatedOn = user.LastActiveOn;
            // mark the row to be inserted on submit
            dataContext.Users.InsertOnSubmit(user);
            // return the user to the calling function
            return user;
        }
        #endregion

        #region GetCartGuid
        private Guid GetCartGuid()
        {
            HttpContext context = HttpContext.Current;
            HttpCookie cookie = context.Request.Cookies["cart"];
            bool fetched = false;
            Guid cartGuid = new Guid();

            // first, check if the user had a cookie set
            if (cookie != null)
            {
                // an exception could occur below, if so, trap it
                try
                {
                    // pull the value from the cookie
                    string encryptedGuid = cookie.Value;
                    // decrypt the cookie value
                    string clearGuid = Encryption.Decrypt(encryptedGuid);
                    // new up a GUID from the decrypted value
                    cartGuid = new Guid(clearGuid);
                    fetched = true;
                }
                catch (Exception) { }
            }
            
            // if no guid was fetched, a new guid is needed
            if (!fetched)
            {
                // get a new guid
                cartGuid = Guid.NewGuid();
                // encrypt the new guid
                string guidString = Encryption.Encrypt(cartGuid.ToString());
                // push the encrypted guid into a cookie
                cookie = new HttpCookie("cart", guidString);
                
                // add the cookie to the response so that it gets set
                context.Response.Cookies.Add(cookie);
                // add the cookie to the request as a convenience
                context.Request.Cookies.Add(cookie);
            }
            
            // return the cart guid
            return cartGuid;
        }
        #endregion

        #region AddToCart
        public void AddToCart()
        {

            System.Web.HttpRequest Request = System.Web.HttpContext.Current.Request;
            ProcureDataContext context = DataFactory.GetDataContext();
            // pull the product id from the request query string / form
            string rId = Request["i"];
            System.Web.HttpContext.Current.Trace.Write("AddToCart", string.Format("    Adding item {0} to cart", rId));
            // also requires quantity in the form of pq[productId]
            UpdateCartProduct(Request, context, rId, UpdateCartMode.Increment);
        }
        #endregion

        #region UpdateCartItem
        public void UpdateCartItem()
        {
            ProcureDataContext context = DataFactory.GetDataContext();
            System.Web.HttpRequest Request = System.Web.HttpContext.Current.Request;
            // find the product id that triggered the update event
            string rId = GetActionId(UpdateInputName);
            // update the product
            UpdateCartProduct(Request, context, rId, UpdateCartMode.Overwrite);
            // submit the changes to the database
            context.SubmitChanges();
        }
        #endregion

        #region UpdateCartProduct
        private void UpdateCartProduct(System.Web.HttpRequest Request, ProcureDataContext contexta, string requestProductId, UpdateCartMode mode)
        {
            ProcureDataContext context = DataFactory.GetDataContext();
            
            Int32 id = 0;
            if (Int32.TryParse(requestProductId, out id))
            {
                string quantityFieldName = QuantityInputName + id.ToString();
                System.Web.HttpContext.Current.Trace.Write("UpdateCartProduct", string.Format("    Pulling Quantity From Field {0}", quantityFieldName));
                if (Request.Form.AllKeys.Contains(quantityFieldName))
                {
                    string rQuantity = Request.Form[quantityFieldName];
                    int quantity = 0;
                    System.Web.HttpContext.Current.Trace.Write("UpdateCartProduct", string.Format("    Checking quantity ", rQuantity));
                    if (Int32.TryParse(rQuantity, out quantity))
                    {
                        Product product = context.Products.FirstOrDefault(p => p.Id == id);
                        System.Web.HttpContext.Current.Trace.Write("UpdateCartProduct", string.Format("    Retrieving Product {0}", requestProductId));
                        if (product != null)
                        {
                            System.Web.HttpContext.Current.Trace.Write("UpdateCartProduct", "    Retrieving Cart");
                            Cart cart = GetCurrentCart(context);
                            System.Web.HttpContext.Current.Trace.Write("UpdateCartProduct", "    Finding Product In Cart");
                            CartProduct cartp = cart.CartProducts.FirstOrDefault(cp => cp.ProductId == id);

                            if (mode == UpdateCartMode.Increment)
                            {
                                System.Web.HttpContext.Current.Trace.Write("UpdateCartProduct", "    Increment Mode");
                                if (cartp != null)
                                {
                                    System.Web.HttpContext.Current.Trace.Write("UpdateCartProduct", string.Format("    Incrementing Existing Quantity {0} by {1}", cartp.Quantity, quantity));
                                    cartp.Quantity += quantity;
                                    System.Web.HttpContext.Current.Trace.Write("UpdateCartProduct", string.Format("    New Quantity = {0}", cartp.Quantity));
                                }
                                else
                                {
                                    System.Web.HttpContext.Current.Trace.Write("UpdateCartProduct", string.Format("    Product {0} not found in cart", requestProductId));
                                    cartp = new CartProduct();
                                    cartp.Product = product;
                                    cartp.Price = product.Price;
                                    cartp.Quantity = quantity;
                                    cart.CartProducts.Add(cartp);
                                }
                            }
                            else if (cartp != null) // update mode is overwrite
                            {
                                System.Web.HttpContext.Current.Trace.Write("UpdateCartProduct", string.Format("    Overwriting Quantity For Product {0}", requestProductId));
                                cartp.Quantity = quantity;
                            }
                            else
                                System.Web.HttpContext.Current.Trace.Write("UpdateCartProduct", string.Format("    Attempt to overwrite quantity for product {0}, but product is not in cart.", requestProductId));

                            System.Web.HttpContext.Current.Trace.Write("UpdateCartProduct", string.Format("    Submitting changes for product {0}.", requestProductId));
                            context.SubmitChanges();
                        }
                    }
                }
            }
        }
        #endregion

        #region UpdateCartItems
        public void UpdateCartItems()
        {
            ProcureDataContext context = DataFactory.GetDataContext();
            System.Web.HttpRequest Request = System.Web.HttpContext.Current.Request;
            // loop through each input in the request form
            foreach (string fieldName in Request.Form.Keys)
            {
                // product id fields will start with the value in ProductIdInputName
                if (fieldName.StartsWith(ProductIdInputName))
                    // update the product
                    UpdateCartProduct(Request, context, Request[fieldName], UpdateCartMode.Overwrite);
            }
            // submit the database changes
            context.SubmitChanges();
        }
        #endregion

        #region RemoveCartItem
        public void RemoveCartItem()
        {
            System.Web.HttpRequest Request = System.Web.HttpContext.Current.Request;
            // find the product id that triggered the removal
            string rId = GetActionId(RemoveInputName);
            int id = 0;
            // make sure the product id is an integer
            if (Int32.TryParse(rId, out id))
            {
                // new up a data context
                ProcureDataContext context = DataFactory.GetDataContext();
                // get the current cart
                Cart cart = GetCurrentCart(context);
                // find the product in the cart
                CartProduct cartp = cart.CartProducts.FirstOrDefault(cp => cp.ProductId == id);
                // if the product could be found
                if (cartp != null)
                    // remove the product from the cart
                    context.CartProducts.DeleteOnSubmit(cartp);
                // submit the changes to the database
                context.SubmitChanges();
            }
        }
        #endregion

        #region ClearCart
        public void ClearCart()
        {
            // new up a data context
            ProcureDataContext context = DataFactory.GetDataContext();
            // get the current cart from the database
            Cart cart = GetCurrentCart(context);
            
            // delete all products from the cart
            context.CartProducts.DeleteAllOnSubmit(cart.CartProducts);

            // submit the changes to the database
            context.SubmitChanges();
        }
        #endregion

        #region GetActionButtonName
        public string GetActionButtonName(string actionPrefix)
        {
            string actionName = string.Empty;

            System.Web.HttpRequest Request = System.Web.HttpContext.Current.Request;
            // loop through all form / query string values
            foreach (string key in Request.Params.AllKeys)
            {
                // check if the key starts with the action prefix
                if (key.ToLower().StartsWith(actionPrefix.ToLower()))
                {
                    // if so, pull the value out
                    string value = Request[key];
                    // check to see if the value is null or empty
                    if (!string.IsNullOrEmpty(value))
                    {
                        // if not, then this is what we were looking for
                        actionName = key;
                        break;
                    }
                }
            }

            // return the id to the calling function
            return actionName;
        }
        #endregion

        #region GetActionId
        public string GetActionId(string actionPrefix)
        {
            // get the action button name
            string actionInputName = GetActionButtonName(actionPrefix);
            // get the product id from the name of the action button
            string id = actionInputName.Substring(actionPrefix.Length);
            return id;
        }
        #endregion
    }
}